package roborally;

import java.util.*;
import be.kuleuven.cs.som.annotate.*;

import roborally.Pieces.*;

/**
 * This is a class for Boards of the Roborally game which has a height, width, maximum height, maximum width, robots, batteries and walls and can be terminated.
 * @version 2.0
 * @author Bart Opsomer 2de Bachelor Ingenieurswetenschappen Electrotechniek-Computerwetenschappen 
 * 		   Peter Opsomer 1ste Master Wiskundige IngenieursTechnieken
 */
public class Board {
	// All the robots on the board
	private Map<Position,Robot> robots;
	// All items on the board that can be picked up by a robot
	private Map<Position,Set<CarriablePiece>> carriablesOnBoard;
	// All the walls on the board
	private Map<Position,Wall> walls;
	
	// The height of the board
	private final long height;
	// The width of the board
	private final long width;

	private boolean terminated;
	
	// the maximum height and width of all boards
	private static final long maxHeight = Long.MAX_VALUE;
	private static final long maxWidth = Long.MAX_VALUE;
	
	/**
	 * Creates a new Board with the given width and height and initializes the fields robots, 
	 * carriablesOnBoard and walls
	 * 
	 * @param	width the width of the board
	 * @param 	height the height of the board
	 * @post	| getRobots() != null && getCarriables() != null && getWalls() != null && !isTerminated()
	 * @throws 	IllegalArgumentException
	 * 			when the given width and height are not valid dimensions for a board
	 * 			| !isValidDimension(width,height)
	 */
	public Board(long width, long height) throws IllegalArgumentException {
		if(isValidDimension(width,height)) {
			this.height = height;
			this.width = width;
		}
		else{
			throw new IllegalArgumentException();
		}
		robots = new HashMap<Position,Robot>();
		carriablesOnBoard =  new HashMap<Position,Set<CarriablePiece>>();
		walls =  new HashMap<Position,Wall>();
		this.terminated =false;
	}
	/**
	 * @return return whether this board is terminated
	 */
	@Basic
	public boolean isTerminated() {
		return this.terminated;
	}
	/**
	 * Terminates all the robots, item and walls on the board and the board itself
	 * @post	the maps robots, walls and carriablesOnBoard are empty
	 * 			| new.robots.isEmpty() == true
	 * 			| new.walls.isEmpty() == true
	 * 			| new.getCarriablesOnBoard.isEmpty() == true
	 * @post	this board is terminated
	 * 			| new.isTerminated() == true
	 * @post	all gamePieces on the board are terminated
	 * 			| forall items in this.getCarriablesOnBoard()
	 * 			|	(new item).isTerminated() == true
	 * 			| forall wall in this.getWalls()
	 * 			|	(new wall).isTerminated() == true
	 * 			| forall robot in this.getRobots()
	 * 			|	(new robot).isTerminated() == true
	 */
	public void terminate() {
		// Terminate all Robots, Walls and Batteries
		for (CarriablePiece item : getCarriablesOnBoard()) {
			item.terminate();
		}
		for (Robot robot : getRobots()) {
			robot.terminate();
		}
		for (Wall wall : getWalls()) {
			wall.terminate();
		}
		// make sure the maps robots, walls and carriablesOnBoard are empty
		robots.clear();
		walls.clear();
		carriablesOnBoard.clear();
		this.terminated = true;
	}
	// 
	/**
	 * Add the CarriablePiece on the board on the given position if possible
	 * 
	 * @param position 	the position on which the item is to be placed
	 * @param item		the item to be added
	 * @post			if item or this is terminated, nothing changes
	 * 					| this.isTerminated()  || item.isTerminated()
	 * 					| then  new = this
	 * @post			the board contains the item
	 * 					| new.getCarriablesOnBoard().contains(item)
	 * @throws 			IllegalArgumentException
	 * 					when it is not possible to add an item on the position
	 * 					| if(walls.containsKey(position))
	 */
	public void addCarriablePiece(Position position, CarriablePiece item) throws IllegalArgumentException{
		// throws IllegalArgumentException if a wall is on the destination location
		if(!isTerminated()  && !item.isTerminated()) {
			if(carriablesOnBoard.containsKey(position)) {
				// Also HashSet.contains works in constant time
				if(carriablesOnBoard.get(position).contains(item)) {
					throw new IllegalArgumentException("Same CarriablePiece cannot be added twice");
				}
				item.setPosition(position);
				carriablesOnBoard.get(position).add(item);
			}
			else if(walls.containsKey(position)) {
				throw new IllegalArgumentException();
			}
			else {
				Set<CarriablePiece> set = new HashSet<CarriablePiece>();
				set.add(item);
				carriablesOnBoard.put(position, set);
				item.setPosition(position);
			}
		}
	}
	/**
	 * Removes the item from the board
	 * 
	 * @param 	item the item to be removed from the board
	 * @post	the item is removed
	 * 			| !new.getCarriablesOnBoard.contains(item)
	 * @throws	IllegalStateException
	 * 			when the item is not on this board
	 * 			if(!carriablesOnBoard.containsKey(item.getPosition)))
	 */
	public void removeItem(CarriablePiece item) {
		Position pos = item.getPosition();
		if (carriablesOnBoard.containsKey(pos)) {
			carriablesOnBoard.get(pos).remove(item);
			item.setPosition(null);
		}
		else{
			throw new IllegalStateException("CarriablePiece was not on Board.");
		}
	}
	/**
	 * Returns all the batteries located on the board
	 * @return  if this is terminated return null
	 * 			| if( this.isTerminated() )
	 * 			| 	then result == null
	 * @return 	the batteries on the board
	 * 			| result = Set<Battery> batteries
	 * 			|	where foreach battery batteries.contains(battery)
	 * 			|		where battery.getPosition.getBoard == this &&
	 * 			|				battery instanceof Battery
	 */
	public Set<Battery> getBatteries(){
		if(!isTerminated()) {
			Set<Battery> result = new HashSet<Battery>();
			for(Set<CarriablePiece> setOfCarriablePieces : carriablesOnBoard.values()) {
					for(CarriablePiece item : setOfCarriablePieces) {
						if(item instanceof Battery)
							result.add((Battery)item);
					}
			}
			return result;
		}
		return null;
	}
	/**
	 * Returns all items on the board
	 * @return  if this is terminated return null
	 * 			| if( this.isTerminated() )
	 * 			| 	then result == null
	 * @return 	the items of the board
	 * 			| result = Set<CarriablePiece> items
	 * 			|	where foreach item items.contains(item)
	 * 			|		where item.getPosition.getBoard == this &&
	 * 			|				item instanceof CarriablePiece
	 */
	@Basic
	public Set<CarriablePiece> getCarriablesOnBoard(){
		if(!isTerminated()) {
			Set<CarriablePiece> result = new HashSet<CarriablePiece>();
			for(Set<CarriablePiece> setOfBatteries : carriablesOnBoard.values()) {
				result.addAll(setOfBatteries);
			}
			return result;
		}
		return null;
	}
	/**
	 * returns the batteries on the given position
	 * @param 	position the position of the batteries that are requested
	 * @return  if this is terminated return null
	 * 			| if( this.isTerminated() )
	 * 			| 	then result == null
	 * @return 	the items on the position
	 * 			| result = Set<Battery> batteries
	 * 			|	where foreach battery batteries.contains(battery)
	 * 			|		where battery.getPosition == position &&
	 * 			|				battery instanceof Battery
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public Set<Battery> getBatteriesAt(Position position) {
		if(isTerminated())
			return null;
		return (Set) carriablesOnBoard.get(position);
	}
	/**
	 * returns all items on the given position
	 * @return  if this is terminated return null
	 * 			| if( this.isTerminated() )
	 * 			| 	then result == null
	 * @return 	the items on the given position
	 * 			| result = Set<CarriablePiece> items
	 * 			|	where foreach item items.contains(item)
	 * 			|		where item.getPosition == position &&
	 * 			|				item instanceof CarriablePiece
	 */
	public Set<CarriablePiece> getItemsAt(Position position) {
		if(isTerminated())
			return null;
		return carriablesOnBoard.get(position);
	}
	/**
	 * returns the robot on the given position
	 * @return  if this is terminated return null
	 * 			| if( this.isTerminated() )
	 * 			| 	then result == null
	 * @return 	the robot on the given position
	 * 			| result = Robot robot
	 * 			|	where robot.getPosition() == position
	 */
	public Robot getRobotAt(Position position) {
		if(isTerminated())
			return null;
		return robots.get(position);
	}
	/**
	 * returns the wall on the given position
	 * @return  if this is terminated return null
	 * 			| if( this.isTerminated() )
	 * 			| 	then result == null
	 * @return 	the wall on the given position
	 * 			| result = Wall wall
	 * 			|	where wall.getPosition() == position
	 */
	public Wall getWallAt(Position position) {
		if(isTerminated())
			return null;
		return walls.get(position);
	}
	/**
	 * Adds the robot to the board if possible
	 * @param 	robot	the robot to be added
	 * @post	The board contains the robot if there was no wall and another robot on that position
	 * 			and this board is not terminated and the robot is not terminated
	 * 			| if( !isTerminated() && !robot.isTerminated() && 
	 * 			| !walls.containsKey(robot.getPosition()) && !robots.containsKey(robot.getPosition() )
	 * 			| 	then new.getRobots.contains(robot) == true
	 * 			| else
	 * 			|	new == this	
	 * @throws  IllegalArgumentException
	 * 			when there is already a wall or another robot on that position
	 * 			| if (walls.containsKey(robot.getPosition())	|| robots.containsKey(robot.getPosition()))
	 */
	public void addRobot(Robot robot) throws IllegalArgumentException {
		// throws IllegalArgumentException if the position contains either a wall or a robot
		if(isTerminated() || robot.isTerminated()) {
			return;
		}
		Position positionOfRobot = robot.getPosition();
		if (walls.containsKey(positionOfRobot)	|| robots.containsKey(positionOfRobot)) {
			throw new IllegalArgumentException();
		}
		robots.put(positionOfRobot,robot);
	}
	/**
	 * remove the robot from the board
	 * @param robot the robot to be removed from the board 
	 * @post	the robot is not on the board
	 * 			| new.getRobots().contains(robot.getPosition) == false
	 */
	public void removeRobot(Robot robot) {
		Position position = robot.getPosition();
		if(robots.containsKey(position)) {
			robots.remove(position);
			robot.setPositionWithOrientation(null);
		}
	}
	/**
	 * returns true if the given position contains a robot
	 * @param position the position in question
	 * @return  if this is terminated return false
	 * 			| if( this.isTerminated() )
	 * 			| 	then result == false
	 * @return 	returns if the position on the board contains a robot
	 * 			| result = getRobots().contain(robot)
	 * 			|	where robot.getPosition() == position
	 */
	public boolean hasRobotAt(Position position) {
		if(isTerminated())
			return false;
		return robots.containsKey(position);
	}
	/**
	 * returns all robots on the on the board
	 * @return  if this is terminated return null
	 * 			| if( this.isTerminated() )
	 * 			| 	then result == null
	 * @return	|result = Set<Robot> robots
	 * 			|	where foreach robot robots.contains(robot)
	 *  		|		where robot.getPosition.getBoard() == this
	 */
	public Set<Robot> getRobots(){
		if(!isTerminated()) {
			return new HashSet<Robot>(robots.values());
		}
		return null;
	}
	/**
	 * returns whether the given dimensions are valid
	 * @param w the possible width
	 * @param h the possible height
	 * @return  if this is terminated return false
	 * 			| if( this.isTerminated() )
	 * 			| 	then result == false
	 * @return returns true 0<w< maxWidth and 0<h< maxHeight else false
	 * 			| result = ((w <= maxWidth) && (w>0) ) &&
				| 	((h <= maxHeight) && (h>0))
	 */
	private boolean isValidDimension(long w, long h) {
		if(!isTerminated()) {
		return ((w <= maxWidth) && (w>0) ) &&
				((h <= maxHeight) && (h>0));
		}
		return false;
	}
	/**
	 * returns the height of the board
	 * @return the height of this board
	 */
	@Basic @Immutable
	public long getHeight() {
		return height;
	}
	/**
	 * returns the width of the board
	 * @return the width of this board
	 */
	@Basic @Immutable
	public long getWidth() {
		return width;
	}
	/**
	 * returns all robots on the on the board
	 * @return  if this is terminated return null
	 * 			| if( this.isTerminated() )
	 * 			| 	then result == null
	 * @return	|result = Set<Wall> walls
	 * 			|	where foreach wall walls.contains(wall)
	 *  		|		where wall.getPosition.getBoard() == this
	 */
	public Set<Wall> getWalls() {
		if(!isTerminated()) {
			return new HashSet<Wall>(walls.values());
		}
		return null;
	}
	/**
	 * adds a wall to the board is possible
	 * @param wall	the wall to be added
	 * @post	The board contains the wall if there was no wall and another robot on that position
	 * 			and this board is not terminated and the wall is not terminated
	 * 			| if( !isTerminated() && !robot.isTerminated() && 
	 * 			| !walls.containsKey(wall.getPosition()) && !robots.containsKey(wall.getPosition() )
	 * 			| 	then new.getWalls().contains(wall) == true
	 * @throws  IllegalArgumentException
	 * 			when there is already a wall or a robot on that position
	 * 			| if (walls.containsKey(robot.getPosition())	|| robots.containsKey(robot.getPosition()))
	 */
	public void addWall(Wall wall) throws IllegalArgumentException {
		// throws IllegalArgumentException if the position contains either a wall or a robot or an carriable item
		if(!isTerminated() && !wall.isTerminated()) {
			Position positionOfWall = wall.getPosition();
			if(walls.containsKey(positionOfWall) || robots.containsKey(positionOfWall) || 
					carriablesOnBoard.containsKey(positionOfWall)) {
				throw new IllegalArgumentException();
			}
			Position pos = wall.getPosition();
			this.walls.put(pos, wall);
		}
	}
	/**
	 * remove the wall from the board
	 * @param wall the wall to be removed from the board 
	 * @post	the wall is not on the board
	 * 			| new.getWalls().contains(wall.getPosition()) == false
	 */
	public void removeWall(Wall wall) {
		Position position = wall.getPosition();
		if(walls.containsKey(position)) {
			walls.remove(position);
			wall.setPosition(null);
		}
	}
	/**
	 * returns if the robot can move 1 step forward
	 * @param robot the to be moved
	 * @return  if this is terminated return false
	 * 			| if( this.isTerminated() )
	 * 			| 	then result == false
	 * @return 	| let 
	 * 			|	nextPositionOfRobot = robot.getPositionWithOrientation().nextPosition() 
	 * 			| in
	 * 			| if(!isValidPosition(nextPositionOfRobot) then result == false
	 * 			| if(robots.containsKey(nextPositionOfRobot.getPosition()) || walls.containsKey(nextPositionOfRobot.getPosition() ) )
	 * 			| 	then result = false
	 * 			| else
	 * 			|	result = true
	 */
	private boolean canMove(Robot robot) {
		if(isTerminated())
			return false;
		boolean authorised = true;
		PositionWithOrientation nextPositionOfRobot;
		try{
			nextPositionOfRobot = robot.getPositionWithOrientation().nextPosition();
		} catch(MoveOutOfBoardException outOfBoard) {
			return false;
		}
		if(robots.containsKey(nextPositionOfRobot.getPosition()) || walls.containsKey(nextPositionOfRobot.getPosition() ) ) {
			authorised =false;
		}
		return authorised;
	}
	/**
	 * Moves the robot 1 step forward
	 * @param myRobot the robot to be moved
	 * @post	if the robot can move, it is moved 1 step forward
	 * 			| if(canMove(myRobot))
	 * 			| 	then (new myRobot).getPosition = myRobot.getPositionWithOrientation().nextPosition() &&
	 * 			|		new.getRobot.contains( myRobot.getPositionWithOrientation().nextPosition())
	 */
	public void moveRobot(Robot myRobot) {
		if(robots.containsValue(myRobot)) {
			if(canMove(myRobot)) {
				robots.remove(myRobot);
				myRobot.move();
				Position pos = myRobot.getPosition();
				robots.put(pos, myRobot);
			}
		}
		
	}
	/**
	 * returns true if the given position contains a wall
	 * @param position the position in question
	 * @return  if this is terminated return false
	 * 			| if( this.isTerminated() )
	 * 			| 	then result == false
	 * @return 	returns if the position on the board contains a wall
	 * 			| result = walls.containsKey(position)
	 */
	public boolean hasWallAt(Position position) {
		if(isTerminated())
			return false;
		return walls.containsKey(position);
	}
	/**
	 * Destroys the given gamePiece
	 * @param gamePiece the gamePiece to be destroyed
	 * @post	the board no longer contains the gamePiece
	 * 			| foreach piece on new board
	 * 			| 	gamePiece != piece
	 */
	private void destroyGamePiece(GamePiece gamePiece) {
		Position targetPosition = gamePiece.getPosition();
		if(robots.containsValue(gamePiece)) {
			robots.remove(targetPosition);
		}
		if(carriablesOnBoard.containsKey(targetPosition)) {
			carriablesOnBoard.get(targetPosition).remove(gamePiece);
		}
		if(walls.containsKey(targetPosition)) {
			walls.remove(targetPosition);
		}
		gamePiece.terminate();
		
	}
	/**
	 * returns true if the given position contains a gamePiece
	 * @param position the position in question
	 * @return  if this is terminated return true
	 * 			| if( this.isTerminated() )
	 * 			| 	then result == true
	 * @return  returns true if a gamePiece has the given position
	 * 			| if (exists piece )
	 * 			|		where piece.getPosition = position
	 * 			| 	then result == true
	 */
	private boolean isOccupiedPosition(Position position) {
		if(isTerminated())
			return true;
		boolean occupied = false;
		if(position == null)
			return true;
		if(robots.containsKey(position) || (carriablesOnBoard.containsKey(position) || walls.containsKey(position)))
			occupied = true;
		return occupied;
	}
	/**
	 * On the closest occupied position to the robot in the orientation of the robot, a random gamePiece is destroyed
	 * @param myRobot the robot that shoots
	 * @post  	if this is terminated nothing changes
	 * 			| if( this.isTerminated() )
	 * 			| 	then new == this
	 * @post	the closest occupied position to the robot in the orientation of the robot, has 1 gamePiece less
	 * 			| let 
	 * 			|	targetPosition = the first position,starting form targetPosition.getPosition(), where isOccupiedPosition(position) &&
	 * 			|		targetPosition.getOrientation() = targetPosition.getPosition().getOrientation()
	 * 			|	List pieces1 =  a list of GamePieces on the targetPosition before the method
	 * 			|	List pieces2 =  a list of GamePieces on the targetPosition after the method
	 * 			| in
	 * 			| pieces2.add(new.getItemsAt(targetPosition)  
	 * 			| pieces2.add(new.getRobotAt(targetPosition)
	 * 			| pieces2.add(new.getWallAt(targetPosition)
	 * 			| pieces1.add(this.getItemsAt(targetPosition)  
	 * 			| pieces1.add(this.getRobotAt(targetPosition)
	 * 			| pieces1.add(this.getWallAt(targetPosition)
	 * 			| pieces1.size() - 1 == pieces2.size()
	 */
	public void shoot(Robot myRobot) {
		if(isTerminated())
			return;
		//Destroy a random object on the first position the robot
		if(myRobot.getEnergy().isGreaterThanOrEquals(myRobot.getShootEnergy())) {
			// From here, 'precondition' is satisfied so nominally
			PositionWithOrientation targetPosition = myRobot.getPositionWithOrientation().nextPosition();
			while(!isOccupiedPosition(targetPosition.getPosition())) {
				try {
					targetPosition = targetPosition.nextPosition();
				}
				catch (MoveOutOfBoardException exc) {
					// there is no object in between the robot and the edge of the board, so return
					return;
				}
			}
			if(walls.containsKey(targetPosition.getPosition())) {
				destroyGamePiece(walls.get(targetPosition.getPosition()));
				myRobot.useEnergy(myRobot.getShootEnergy());
				return;
			}
			Set<GamePiece> pieces = new HashSet<GamePiece>();
			if(robots.containsKey(targetPosition.getPosition())) {
				pieces.add(robots.get(targetPosition.getPosition()));
			}
			if(carriablesOnBoard.containsKey(targetPosition.getPosition())) {
				pieces.addAll(carriablesOnBoard.get(targetPosition.getPosition()));
			}
			
			if(pieces.isEmpty())
				return;
			Random rand = new Random();
			GamePiece[] arrayOfGamePieces = new GamePiece[pieces.size()];
			arrayOfGamePieces = pieces.toArray(arrayOfGamePieces);
			
			int nextIntRand = rand.nextInt(arrayOfGamePieces.length);
			GamePiece pieceToBeRemoved = arrayOfGamePieces[nextIntRand];
	
			destroyGamePiece(pieceToBeRemoved);	
			myRobot.useEnergy(myRobot.getShootEnergy());
		}
	}
	/**
	 * Put all GamePieces of secondBoard on this board on their corresponding place, if possible
	 * @param secondBoard the board to be merged into this board
	 * @post	if this or secondBoard is terminated return null
	 * 			| if( this.isTerminated() || secondBoard.isTerminated() )
	 * 			| 	then new == this && new secondBoard == secondBoard
	 * @post	all GamePieces of secondBoard are put on this board on their corresponding place, if possible
	 * 			| foreach piece on secondBoard
	 * 			|	where piece.setBoard(this) is valid
	 * 			| new.contains(piece) &&
	 * 			| new secondBoard.isTerminated()
	 */
	public void merge(Board secondBoard) {
		if(isTerminated() || secondBoard.isTerminated())
			return;
		if(secondBoard == this)
			return;
		for(CarriablePiece item : secondBoard.getCarriablesOnBoard()) {
			try {
				Position position = item.getPosition().getSamePositionOnOtherBoard(this);
				secondBoard.removeItem(item);
				item.setPosition(position);
				this.addCarriablePiece(item.getPosition(),item);
			}
			catch(Exception e){
				// a wall is on the corresponding place on this or invalid new position, so terminate!
				item.terminate();
			}
		}
		for(Robot robot : secondBoard.getRobots()) {
			if(isValidPosition(robot.getPosition().getX(), robot.getPosition().getY())) {
				try {
					Position pos = robot.getPosition().getSamePositionOnOtherBoard(this);
					PositionWithOrientation positionwO = PositionWithOrientation.getPWO(pos.getX(), pos.getY(), pos.getBoard(), robot.getPositionWithOrientation().getOrientation());
					secondBoard.removeRobot(robot);
					robot.setPositionWithOrientation(positionwO);
					this.addRobot(robot);
				}
				catch(Exception e){
					robot.terminate();
				}
			}
		}
		for (Wall wall : secondBoard.getWalls()) {
			if(isValidPosition(wall.getPosition().getX(), wall.getPosition().getY())) {
				try {
					Position position = wall.getPosition().getSamePositionOnOtherBoard(this);
					secondBoard.removeWall(wall);
					wall.setPosition(position);
					addWall(wall);
				}
				catch(Exception e){
					// (a wall or a robot is on the corresponding place on this // don't do anything) terminate!
					wall.terminate();
				}
			}
		}
		// Walls, Robots or Batteries that were not added to this Board should have been terminated by now, so that we don't 
		// have to call secondBoard.terminate(); which would also terminate Robots etc that were effectively moved
		secondBoard.terminated = true;
	}
	/**
	 * returns if x and y are valid coordinates
	 * @return  if this is terminated return false
	 * 			| if( this.isTerminated() )
	 * 			| 	then result == false
	 * @param x the x-axis coordinate
	 * @param y the y-axis coordinate
	 * @return 	| result = Position.isValidPosition(this, x, y);
	 */
	private boolean isValidPosition(long x,long y) {
		if(isTerminated())
			return false;
		return Position.isValidPosition(this, x, y);
	}
	/**
	 * Add walls on the diagonal from (x1,y1) to (x2,y2)
	 * @param x1 the x-axis coordinate of the first point
	 * @param x2 the x-axis coordinate of the second point
	 * @param y1 the y-axis coordinate of the first point
	 * @param y2 the y-axis coordinate of the second point
	 * @post 	if this board is terminated, nothing changes
	 * 			| if(isTerminated())
	 * 			|	then new == this
	 * @post 	walls were added on the diagonal from (x1,y1) to (x2,y2)
	 * 			| let y = y1 if x1 <= x2, else y2
	 * 			| for i in [0, abs(x1-x2) ] : new.hasWallAt(Position.getPosition(min(x1,x2)+i, y - i,this) )
	 * @throws 	IllegalArgumentException
	 * 			the given positions do not form a diagonal
	 * 			| abs(x2-x1) != abs(y2-y1)
	 */
	public void addDiagonalWalls(long x1,long x2,long y1, long y2) {
		if(isTerminated())
			return;
		if(Math.abs(x2-x1) != Math.abs(y2-y1) ) {
			throw new IllegalArgumentException("No diagonal was given.");
		}
		long toNextX = -1;
		if(x1 < x2) {
			toNextX = 1;
		}
		long y = y1;
		long toNextY = -1;
		if(y1 < y2) {
			toNextY = 1;
		}
		// toNextX*(x2-x) >= 0 so that actually x2 >= x if x1 < x2 with x increasing  and x2 <= x if x1 >= x2  with x decreasing
		for(long x = x1; toNextX*(x2-x) >= 0; x += toNextX) {
			Wall nextWallToPlace = new Wall();
			nextWallToPlace.setPosition(Position.getPosition(x,y,this) );
			addWall(nextWallToPlace); // assume no wall is here yet
			y = y + toNextY;
		}
	}
}
